package com.gcxy.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gcxy.constant.StringConst;
import com.gcxy.core.PageVO;
import com.gcxy.core.R;
import com.gcxy.dto.user.UserPageDTO;
import com.gcxy.dto.user.PasswordDTO;
import com.gcxy.dto.user.UserInfoDTO;

import com.gcxy.entity.*;
import com.gcxy.mapper.*;
import com.gcxy.service.*;
import com.gcxy.utils.*;
import com.gcxy.vo.agency.AgencyVO;
import com.gcxy.vo.resource.ResourceVO;
import com.gcxy.vo.role.RoleVO;
import com.gcxy.vo.semester.SemesterVO;
import com.gcxy.vo.user.UserPageVO;
import com.gcxy.vo.user.UserVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

import static com.gcxy.constant.StringConst.*;

/**
 * @author pep7chiao
 * @date 2024/7/7
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Resource
    private RoleMapper roleMapper;
    @Resource
    private UserMapper userMapper;
    @Resource
    private UserRoleService userRoleService;

    @Value("${jwt.tokenHeader}")
    private String tokenHeader;

    @Value("${jwt.expiration}")
    private Integer expiration;
    @Resource
    private RedisTemplateUtil redisTemplateUtil;
    @Resource
    private UserDetailsService userDetailsService;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private AgencyService agencyService;

    @Autowired
    private ResourceService resourceService;

    @Autowired
    private SemesterService semesterService;

    @Resource
    private ClazzService clazzService;

    @Resource
    private PositionService positionService;

    @Resource
    private StudentTeacherService studentTeacherService;

    /**
     * 新增用户
     *
     * @param userInfoDTO 用户数据传递对象
     * @return 前端响应对象
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void addUser(UserInfoDTO userInfoDTO) {
        //查询用户账号和名称是否已经存在
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>()
                .eq(User::getUsername, userInfoDTO.getUsername());
        User selectUser = getOne(queryWrapper);
        if (!ObjectUtils.isEmpty(selectUser)) {
            throw new RuntimeException("用户账号重复");
        }
        User user = BeanCopyUtil.copy(userInfoDTO, User.class);
        user.setEnabled(true);
        user.setPassword(passwordEncoder.encode(StringConst.DEFAULT_PASSWORD));
        // 新增用户信息
        boolean save = save(user);
        if (!save) {
            throw new RuntimeException("新增用户失败");
        }
        // 根据用户信息获取到用户ID
        Integer userId = getOne(new LambdaQueryWrapper<User>().eq(User::getUsername, userInfoDTO.getUsername())).getId();
        List<Integer> roles = userInfoDTO.getRoles();
        for (Integer roleId : roles) {
            // 为新增用户赋值角色
            UserRole userRole = new UserRole();
            userRole.setUserId(userId);
            Role role = roleMapper.selectById(roleId);
            if (ObjectUtils.isEmpty(role)) {
                throw new RuntimeException("用户绑定角色{" + roleId + "}不存在");
            }
            userRole.setRoleId(roleId);
            userRoleService.save(userRole);
        }
    }

    /**
     * 修改
     *
     * @param userInfoDTO 用户数据传递对象
     * @return 前端响应对象
     */
    @Override
    public R updateUser(UserInfoDTO userInfoDTO) {
        if (ObjectUtils.isEmpty(userInfoDTO.getId())) {
            return R.Failed("未指定需要修改的用户");
        }
        User user = getById(userInfoDTO.getId());
        if (ObjectUtils.isEmpty(user)) {
            return R.Failed("未找到需要修改的用户");
        }
        if (!user.getUsername().equals(userInfoDTO.getUsername())) {
            return R.Failed("用户账号不能修改");
        }
        BeanUtils.copyProperties(userInfoDTO, user);
        // 更新用户信息
        boolean update = updateById(user);
        if (!update) {
            return R.Failed("用户修改失败");
        }
        // 根据用户信息获取到用户ID
        userRoleService.removeByUserId(userInfoDTO.getId());
        if (CollectionUtils.isNotEmpty(userInfoDTO.getRoles())) {
            for (Integer roleId : userInfoDTO.getRoles()) {
                // 为新增用户赋值角色
                UserRole userRole = new UserRole();
                userRole.setUserId(userInfoDTO.getId());
                userRole.setRoleId(roleId);
                userRoleService.save(userRole);
            }
        }
        return R.Success("修改用户成功！");
    }

    /**
     * 根据条件分页获取用户列表
     *
     * @param userPageDTO 查询条件对象
     * @return 分页对象
     */
    @Override
    public PageVO<UserPageVO> getUserPageByCondition(UserPageDTO userPageDTO) {
        // 开启分页
        Page<User> page = new Page<>(userPageDTO.getPageNo(), userPageDTO.getPageSize());
        //获取组织条件
        List<Integer> agencyIds = new ArrayList<>();
        if (StringUtils.isNotEmpty(userPageDTO.getAgencyName())) {
            List<Agency> agencyList = agencyService.list(new LambdaQueryWrapper<Agency>().like(Agency::getName, userPageDTO.getAgencyName()));
            if (CollectionUtils.isNotEmpty(agencyList)) {
                agencyIds = agencyList.stream().map(Agency::getId).collect(Collectors.toList());
            }
        }
        //获取角色条件
        List<Integer> userIds = new ArrayList<>();
        if (StringUtils.isNotEmpty(userPageDTO.getRoleName())) {
            userIds = userRoleService.getUserByRoleName(userPageDTO.getRoleName());
        }
        // 定义查询条件
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(userPageDTO.getUsername()), User::getUsername, userPageDTO.getUsername())
                .like(StringUtils.isNotBlank(userPageDTO.getFullName()), User::getFullName, userPageDTO.getFullName())
                .in(CollectionUtils.isNotEmpty(agencyIds), User::getAgencyId, agencyIds)
                .in(CollectionUtils.isNotEmpty(userIds), User::getId, userIds)
                .orderByDesc(User::getUpdateTime);
        Page<User> userPage = page(page, queryWrapper);
        PageVO<UserPageVO> userVOPageVO = new PageVO<>();
        BeanUtils.copyProperties(userPage, userVOPageVO);
        List<UserPageVO> userPageVOS = new ArrayList<>();
        for (User user : userPage.getRecords()) {
            UserPageVO userPageVO = BeanCopyUtil.copy(user, UserPageVO.class);
            //组织信息
            if (user.getAgencyId() != null) {
                Agency agency = agencyService.getById(user.getAgencyId());
                AgencyVO agencyVO = BeanCopyUtil.copy(agency, AgencyVO.class);
                userPageVO.setAgencyVO(agencyVO);
            }
            //角色信息
            List<RoleVO> roleVOList = userRoleService.getRoleByUserId(user.getId());
            userPageVO.setRoles(roleVOList);
            userPageVOS.add(userPageVO);
        }
        userVOPageVO.setRecords(userPageVOS);
        return userVOPageVO;
    }


    /**
     * 用户更新密码
     *
     * @param passwordDTO 用户密码参数对象
     * @return 返回结果对象
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public R<?> updatePassword(PasswordDTO passwordDTO) {
        // 判断新旧密码是否一致
        if (passwordEncoder.matches(passwordDTO.getNewPassword(), passwordDTO.getOldPassword())) {
            return R.Failed("新密码不能与旧密码相同");
        }
        // 获取当前登录用户的ID
        Integer userId = UserUtil.getCurrentUser().getId();
        // 通过用户ID查询到对应的用户对象
        User user = getById(userId);
        //密码加密
        String password = passwordEncoder.encode(passwordDTO.getNewPassword());
        // 更新用户密码
        user.setPassword(password);
        boolean update = updateById(user);
        if (!update) {
            return R.Failed("更新密码失败");
        }
        return R.Success("更新密码成功");
    }

    /**
     * 批量重置用户密码
     *
     * @param userIds
     * @return
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public R<?> resetPassword(List<Integer> userIds) {
        // 获取当前登录用户的角色信息
        List<Role> roles = UserUtil.getCurrentUser().getRoles();
        boolean contains = roles.stream().map(Role::getId).collect(Collectors.toList()).contains(SUPER_ADMIN);
        if (!contains) {
            return R.Failed("非超级管理员不可以重置密码");
        }
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.in(User::getId, userIds)
                .set(User::getPassword, passwordEncoder.encode(StringConst.DEFAULT_PASSWORD));
        boolean update = update(updateWrapper);
        if (!update) {
            return R.Failed("重置密码失败");
        }
        return R.Success("重置密码成功");
    }


    /**
     * 根据用户ID删除用户
     *
     * @param userId 用户ID
     * @return 前端响应对象
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public R deleteUserById(Integer userId) {
        // 获取当前登录用户的角色信息
        List<Role> roles = UserUtil.getCurrentUser().getRoles();
        boolean contains = roles.stream().map(Role::getId).collect(Collectors.toList()).contains(SUPER_ADMIN);
        if (!contains) {
            return R.Failed("非超级管理员不可以删除用户");
        }
        User user = getById(userId);
        UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername());
        // 1. 删除user表中的数据
        removeById(userId);
        // 3. 删除user_role表中的数据
        userRoleService.remove(new LambdaQueryWrapper<UserRole>().eq(UserRole::getUserId, userId));
        // 4. 删除Redis中的数据
        // 通过userDetails生成token
        String token = jwtTokenUtil.generateToken(userDetails);
        //刷新token
        jwtTokenUtil.refreshToken(token);
        //  删除Redis中的数据
        redisTemplateUtil.remove(token);
        return R.Success("删除成功！", null);
    }

    /**
     * 根据用户名称和角色模糊查询关联用户信息
     *
     * @param fullName
     * @param roleId
     * @return
     */
    @Override
    public List<User> getUserByUserNameAndRole(String fullName, Integer roleId) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        //获取当前角色对应的用户
        List<Integer> userIds = userRoleService.getAllUserByRoleId(roleId);
        if (CollectionUtils.isEmpty(userIds)) {
            log.debug("角色{" + roleId + "}未绑定用户信息");
            return new ArrayList<>();
        }
        queryWrapper.like(StringUtils.isNotEmpty(fullName), User::getFullName, fullName)
                .in(User::getId, userIds);
        return list(queryWrapper);
    }

    /**
     * 根据id获取用户信息
     *
     * @return 当前用户对象
     */
    @Override
    public UserVO getUserInfo() {
        // 如果id为空，获取当前登录用户的ID
        User user = UserUtil.getCurrentUser();
        UserVO userVO = BeanCopyUtil.copy(user, UserVO.class);
        //组织信息
        if (ObjectUtil.isNotEmpty(user.getAgencyId())) {
            Agency agency = agencyService.getById(user.getAgencyId());
            AgencyVO agencyVO = BeanCopyUtil.copy(agency, AgencyVO.class);
            userVO.setAgency(agencyVO);
        }
        // 设置用户的角色
        List<RoleVO> roleVOList = userRoleService.getRoleByUserId(user.getId());
        userVO.setRoles(roleVOList);
        //设置资源
        if (CollectionUtils.isNotEmpty(roleVOList)) {
            List<ResourceVO> allResourceVOList = new ArrayList<>();
            HashSet<ResourceVO> resourceVOHashSet = new HashSet<>();
            roleVOList.forEach(roleVO -> {
                List<ResourceVO> resourceVOList = resourceService.getResourceListByRoleId(roleVO.getId());
                if (CollectionUtils.isNotEmpty(resourceVOList)) {
                    for (ResourceVO resourceVO : resourceVOList) {
                        if (resourceVOHashSet.add(resourceVO)) {
                            allResourceVOList.add(resourceVO);
                        }
                    }
                }
            });
            userVO.setUserResource(allResourceVOList);
        }
        //获取学期信息
        Semester semester = semesterService.getCurrentSemester();
        SemesterVO semesterVO = BeanCopyUtil.copy(semester, SemesterVO.class);
        userVO.setSemester(semesterVO);
        return userVO;
    }

    /**
     * 通过用户名获取用户对象
     *
     * @param username 用户名
     * @return 用户对象
     */
    @Override
    public User getUserByUserName(String username) {
        return getOne(new QueryWrapper<User>().eq("username", username));
    }

    @Override
    public List<User> getUserListByUserName(String username) {
        return list(new LambdaQueryWrapper<User>().like(User::getFullName, username));
    }


    /**
     * 用户登录后返回token
     *
     * @param username 用户名
     * @param password 密码
     * @param code     验证码
     * @param request  请求
     * @return 携带token的返回对象
     */
    @Override
    @Transactional
    public R login(String username, String password, String code, HttpServletRequest request) {
        // 1. -------------------- 判断验证码 --------------------
        // 从Redis中获取验证码
//        String kaptcha = (String) redisTemplate.opsForValue().get(KAPTCHA);
//        System.out.println(username + "  " + password + "  " + kaptcha);
        // 判断验证码是否正确
//        if (StringUtils.isEmpty(kaptcha) || !kaptcha.equals(code)) {
//            return R.Failed("验证码输入错误，请重新输入");
//        }
        // 2. -------------------- 判断用户名和密码 --------------------
        // 获取到UserDetails
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        // 如果userDetails为空或密码匹配不一致
        if (null == userDetails || !passwordEncoder.matches(password, userDetails.getPassword())) {
            return R.Failed("用户名或密码错误");
        }

        // 3. -------------------- 判断账号是否被禁用 --------------------
        // 查看用户是否被禁用
//        if (!userDetails.isEnabled()) {
//            return R.Failed("当前账号已禁用，请联系管理员");
//        }

        // 4. -------------------- 更新用户登录信息 --------------------
//        updateLastLoginInfo(username, request);

        // 更新security登录用户对象，参数1：userDetails，参数2：密码，参数3：用户权限列表
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        // security全局更新用户权限
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        // 通过userDetails生成token
        String token = jwtTokenUtil.generateToken(userDetails);

        Map<String, String> tokenMap = new HashMap<>();
        // 令牌
        tokenMap.put("token", token);
        //把token作为key,把username作为value存入redis
        redisTemplateUtil.setString(token, username);
        redisTemplateUtil.setMinuteExpire(token, expiration / 24 / 60);
        // 清除安全上下文
        SecurityContextHolder.clearContext();
        // 登录成功后将token返回给前端
        return R.Success("登录成功", tokenMap);
    }


    /**
     * 用户登录后返回token
     *
     * @param username 用户名
     * @param password 密码
     * @param request  请求
     * @return 携带token的返回对象
     */
    @Override
    public R login(String username, String password, HttpServletRequest request) {
        return login(username, password, null, request);
    }

    /**
     * 通过用户ID获取对应的用户权限
     *
     * @param userId 用户ID
     * @return 当前用户的权限集合
     */
    @Override
    public List<Role> getRoles(Integer userId) {
        return roleMapper.getRoles(userId);
    }

    @Override
    public R<Void> logout(HttpServletRequest request) {
        //  从当前请求中获取到授权数据
        String authHeader = request.getHeader(tokenHeader);
        if (authHeader != null) {
            //  删除Redis中的数据
            redisTemplateUtil.remove(authHeader);
            // 从token中获取到当前登录用户的用户名
            String username = jwtTokenUtil.getUserNameFromToken(authHeader);
            // 获取到UserDetails
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            // 通过userDetails生成token
            String token = jwtTokenUtil.generateToken(userDetails);
            //刷新token
            jwtTokenUtil.refreshToken(token);
        }
        return R.Success("退出成功！");
    }


    /**
     * 根据角色id判断当前角色是否是角色id对应的角色
     *
     * @param roleId
     * @return
     */
    public boolean checkUserRole(Integer roleId) {
        List<RoleVO> roleList = getUserInfo().getRoles();
        if (!roleList.isEmpty()) {
            return roleList.stream()
                    .map(RoleVO::getId)
                    .anyMatch(id -> id.equals(roleId));
        }
        return false;
    }

    /**
     * 根据资源路径判断该用户是否有权限访问该资源
     *
     * @param resourcePath
     * @return
     */
    public boolean isInvalidUserResourcePermission(String resourcePath) {
        List<ResourceVO> resourceVOList = getUserInfo().getUserResource();
        return resourceVOList.stream()
                .anyMatch(resourceVO -> !resourceVO.getResourcePath().equals(resourcePath));
    }

    /**
     * 根据机构id获取机构下的所有用户
     *
     * @param agencyId
     * @return
     */
    @Override
    public List<User> getStudentByAgencyId(Integer agencyId) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getAgencyId, agencyId);
        List<User> list = list(wrapper);
        List<Integer> studentIds = userRoleService.getAllUserByRoleId(STUDENT);
        if (!studentIds.isEmpty()) {
            list.removeIf(user -> !studentIds.contains(user.getId()));
        }
        return list;
    }

    /**
     * 获取当前组织下的老师信息
     *
     * @param agencyId
     * @return
     */
    @Override
    public List<User> getTeacherByAgencyId(Integer agencyId) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getAgencyId, agencyId);
        List<User> list = list(wrapper);
        List<Integer> teacherIds = userRoleService.getAllUserByRoleId(TEACHER);
        if (!teacherIds.isEmpty()) {
            list.removeIf(user -> !teacherIds.contains(user.getId()));
        }
        return list;
    }

    /**
     * 获取学期内组织下的名誉教师信息
     *
     * @param agencyId
     * @param semesterId
     * @return
     */
    @Override
    public List<User> getTeacherByAgencyId(Integer agencyId, Integer semesterId) {
        List<StudentTeacher> teacherSemester = studentTeacherService.getTeacherBySemesterId(semesterId);
        if (teacherSemester.isEmpty()) {
            return new ArrayList<>();
        }
        List<Integer> semesterTeacherIds = teacherSemester.stream().map(StudentTeacher::getHonoraryTeacherId).collect(Collectors.toList());
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getAgencyId, agencyId);
        List<User> list = list(wrapper);
        List<Integer> teacherIds = userRoleService.getAllUserByRoleId(TEACHER);
        if (!teacherIds.isEmpty()) {
            list.removeIf(user -> !teacherIds.contains(user.getId()));
            list.removeIf(user -> !semesterTeacherIds.contains(user.getId()));
        }
        return list;
    }
}